Google Β· Notion Β· San Francisco
| Day | Session | Time |
|---|---|---|
| Mon | Python syntax drills + 2 easy problems | 60 min |
| Tue | Data structures deep dive (see problems below) | 75 min |
| Wed | Write 5 STAR behavioral stories | 60 min |
| Thu | Arrays & strings pattern block | 75 min |
| Fri | Review solutions + flashcard pass | 45 min |
| # | Problem | Diff | |
|---|---|---|---|
| 1 | Two Sum | Easy | |
| 217 | Contains Duplicate | Easy | |
| 242 | Valid Anagram | Easy | |
| 121 | Best Time to Buy and Sell Stock | Easy | |
| 49 | Group Anagrams | Medium | |
| 238 | Product of Array Except Self | Medium | |
| 560 | Subarray Sum Equals K | Medium |
| # | Problem | Diff | |
|---|---|---|---|
| 206 | Reverse Linked List | Easy | |
| 21 | Merge Two Sorted Lists | Easy | |
| 20 | Valid Parentheses | Easy | |
| 141 | Linked List Cycle | Easy | |
| 19 | Remove Nth Node From End of List | Medium | |
| 138 | Copy List with Random Pointer | Medium | |
| 739 | Daily Temperatures | Medium | |
| 394 | Decode String | Medium |
| Day | Session | Time |
|---|---|---|
| Mon | Pattern block β 2 mediums, 25 min each | 75 min |
| Tue | System design deep dive (one topic, see curriculum) | 60 min |
| Wed | Behavioral practice out loud + pattern problems | 60 min |
| Thu | Pattern block β 2 more mediums | 75 min |
| Fri | Weak spot review + 1 easy warmup | 45 min |
| # | Problem | Diff | |
|---|---|---|---|
| 125 | Valid Palindrome | Easy | |
| 167 | Two Sum II | Medium | |
| 15 | 3Sum | Medium | |
| 11 | Container With Most Water | Medium | |
| 3 | Longest Substring Without Repeating Characters | Medium | |
| 424 | Longest Repeating Character Replacement | Medium | |
| 76 | Minimum Window Substring | Hard | |
| 33 | Search in Rotated Sorted Array | Medium | |
| 153 | Find Minimum in Rotated Sorted Array | Medium | |
| 981 | Time Based Key-Value Store | Medium |
| # | Problem | Diff | |
|---|---|---|---|
| 104 | Maximum Depth of Binary Tree | Easy | |
| 102 | Binary Tree Level Order Traversal | Medium | |
| 199 | Binary Tree Right Side View | Medium | |
| 98 | Validate Binary Search Tree | Medium | |
| 236 | Lowest Common Ancestor of a Binary Tree | Medium | |
| 200 | Number of Islands | Medium | |
| 133 | Clone Graph | Medium | |
| 207 | Course Schedule | Medium | |
| 417 | Pacific Atlantic Water Flow | Medium | |
| 78 | Subsets | Medium | |
| 46 | Permutations | Medium | |
| 39 | Combination Sum | Medium | |
| 79 | Word Search | Medium |
| # | Problem | Diff | |
|---|---|---|---|
| 70 | Climbing Stairs | Easy | |
| 198 | House Robber | Medium | |
| 322 | Coin Change | Medium | |
| 91 | Decode Ways | Medium | |
| 139 | Word Break | Medium | |
| 62 | Unique Paths | Medium | |
| 1143 | Longest Common Subsequence | Medium | |
| 215 | Kth Largest Element in an Array | Medium | |
| 347 | Top K Frequent Elements | Medium | |
| 621 | Task Scheduler | Medium | |
| 295 | Find Median from Data Stream | Hard |
| Day | Session | Time |
|---|---|---|
| Mon | Full mock coding interview β no hints, no pausing | 60 min |
| Tue | System design full mock (45 min) + debrief | 60 min |
| Wed | Hard problem β 45 min attempt, then study solution | 75 min |
| Thu | Full mock coding interview | 60 min |
| Fri | Behavioral mock + company research | 60 min |
| # | Problem | Diff | |
|---|---|---|---|
| 42 | Trapping Rain Water | Hard | |
| 23 | Merge K Sorted Lists | Hard | |
| 297 | Serialize and Deserialize Binary Tree | Hard | |
| 127 | Word Ladder | Hard | |
| 146 | LRU Cache | Medium |
Notion
| # | Step | Notes |
|---|---|---|
| 1 | Clarify the problem | Restate it in your own words. Let the interviewer correct misunderstandings early before you've written any code. |
| 2 | Ask about constraints and edge cases | Input bounds, nulls, empty inputs, duplicates, negatives. Shows defensive thinking. Ask explicitly: "Can the input be empty?" "Can values be negative?" |
| 3 | Talk through your approach before coding | Most skipped step. Start with brute force, then optimize. Get the interviewer to confirm your direction before touching code β you might be heading somewhere wrong, or there's a better approach they're looking for. |
| 4 | Write the code | Talk through what you're doing as you go. Silence is a red flag. If stuck on a detail, say so and move on β come back to it. |
| 5 | Test your code | Trace through test cases on the code you wrote, not the algorithm in your head. This catches off-by-one errors and missed edge cases that feel correct mentally but aren't in the actual code. |
| 6 | Discuss complexity | Time and space, and explain why. O(n) isn't enough if it's actually O(n log n). Be precise. |
| Company | Notes | Apply |
|---|---|---|
| Recruiter outreach or referral strongly preferred | Week 6 | |
| Notion | Apply direct + find an employee referral | Week 6 |
| Linear | Smaller team, moves fast, strong eng culture | Week 6 |
| Figma | Heavy frontend, design-system work | Week 6 |
| Vercel | Full-stack, strong eng culture | Week 7 |
| Stripe | High bar, very well-run process | Week 7 |
| Anthropic | If open to AI product work | Week 7 |
Core Data Structures
# Lists arr = [1, 2, 3] arr.append(4) last = arr.pop() # removes and returns last arr[0] # first element arr[-1] # last element arr[1:3] # slice: index 1 inclusive to 3 exclusive arr[::-1] # reversed copy len(arr) # Sets β O(1) average membership s = {1, 2, 3} empty_set = set() s.add(4) s.discard(2) # no error if missing; .remove() raises KeyError if 3 in s: ... unique_vals = set(arr) # Dicts mp = {'a': 1, 'b': 2} mp['c'] = 3 val = mp.get('d', 0) # 0 if 'd' not in mp β no KeyError if 'a' in mp: ... del mp['a'] # Tuples β immutable; hashable (can be dict keys or set members) t = (1, 2, 3) t[0] list(t) # convert to list tuple([1, 2, 3]) # convert from list
Iteration Patterns
# Arrays β prefer enumerate over range(len(...)) for x in arr: ... for i in range(len(arr)): ... for i, x in enumerate(arr): ... # index + value together for x in reversed(arr): ... # range(start, stop, step) β stop is exclusive range(5, 10) # 5, 6, 7, 8, 9 range(0, 10, 2) # 0, 2, 4, 6, 8 range(10, 0, -1) # 10, 9, ..., 1 β count down # Dicts for k in mp: ... # keys for v in mp.values(): ... for k, v in mp.items(): ... # key + value (most common) # Sets β order not guaranteed for x in s: ... for x in sorted(s): ... # if you need a stable order
2D Grids
# Initialize β m rows of n columns rows, cols = 3, 4 grid = [[0] * cols for _ in range(rows)] # CORRECT β independent rows # bad_grid = [[0] * cols] * rows # WRONG β all rows are the same object # Iterate by index for r in range(rows): for c in range(cols): val = grid[r][c] # Iterate with enumerate (index + value) for r, row in enumerate(grid): for c, val in enumerate(row): print(r, c, val) # 4-direction deltas (up, down, left, right) dirs_4 = [(-1, 0), (1, 0), (0, -1), (0, 1)] # 8-direction deltas (including diagonals) dirs_8 = [(-1,0),(1,0),(0,-1),(0,1),(-1,-1),(-1,1),(1,-1),(1,1)] # Bounds check helper rows, cols = len(grid), len(grid[0]) in_bounds = lambda r, c: 0 <= r < rows and 0 <= c < cols for dr, dc in dirs_4: nr, nc = r + dr, c + dc if in_bounds(nr, nc): ... # safe to access grid[nr][nc]
Algorithm Templates
# Two pointers l, r = 0, len(arr) - 1 # Sliding window from collections import defaultdict window = defaultdict(int) l = 0 for r in range(len(s)): window[s[r]] += 1 while invalid(window): window[s[l]] -= 1 l += 1 # BFS β graph from collections import deque q = deque([start]) visited = {start} while q: node = q.popleft() for nei in graph[node]: if nei not in visited: visited.add(nei) q.append(nei) # BFS β 2D grid from collections import deque def bfs_grid(grid, start_r, start_c): rows, cols = len(grid), len(grid[0]) dirs = [(-1,0),(1,0),(0,-1),(0,1)] in_bounds = lambda r, c: 0 <= r < rows and 0 <= c < cols q = deque([(start_r, start_c)]) visited = {(start_r, start_c)} while q: r, c = q.popleft() for dr, dc in dirs: nr, nc = r + dr, c + dc if in_bounds(nr, nc) and (nr, nc) not in visited: visited.add((nr, nc)) q.append((nr, nc)) # DFS β iterative stack = [start] visited = {start} while stack: node = stack.pop() for nei in graph[node]: if nei not in visited: visited.add(nei) stack.append(nei) # Binary search β lo/hi loop def binary_search(arr, target): lo, hi = 0, len(arr) - 1 while lo <= hi: mid = (lo + hi) // 2 if arr[mid] == target: return mid elif arr[mid] < target: lo = mid + 1 else: hi = mid - 1 return -1
Common Gotchas
# 1. Mutable default arguments β shared across all calls def f_bad(x, arr=[]): # BAD arr.append(x); return arr def f_good(x, arr=None): # GOOD if arr is None: arr = [] arr.append(x); return arr # 2. is vs == a, b = [1, 2], [1, 2] a == b # True β same value a is b # False β different objects; use `is` only for None checks if x is None: ... # 3. Integer vs float division 5 / 2 # 2.5 (float) 5 // 2 # 2 (floor); use for index math: mid = (lo + hi) // 2 # 4. sort() vs sorted() β sort() mutates and returns None arr.sort() # in-place, returns None new_arr = sorted(arr) # returns new list arr = arr.sort() # BUG β arr is now None # 5. Modifying a list while iterating over it for x in arr: if condition(x): arr.remove(x) # BAD β skips elements new_arr = [x for x in arr if not condition(x)] # GOOD # 6. range() is end-exclusive range(n) # 0 .. n-1 range(start, end) # start .. end-1 for i in range(1, n + 1): ... # inclusive 1..n # 7. any() / all() if any(x > 0 for x in arr): ... # at least one satisfies if all(x >= 0 for x in arr): ... # every element satisfies
collections.defaultdict
# Never KeyError β missing keys auto-initialize graph = defaultdict(list) graph['a'].append('b') # no need to check if 'a' exists first freq = defaultdict(int) for c in "banana": freq[c] += 1 # starts at 0 automatically
| # | Problem | Diff |
|---|---|---|
| 49 | Group Anagrams | Medium |
| 207 | Course Schedule | Medium |
| 133 | Clone Graph | Medium |
collections.Counter
from collections import Counter
c = Counter("banana") # Counter({'a': 3, 'n': 2, 'b': 1})
c.most_common(2) # [('a', 3), ('n', 2)]
c['z'] # 0 β no KeyError
# Subtract two counters
c1 = Counter("abc")
c2 = Counter("ab")
c1 - c2 # Counter({'c': 1})
| # | Problem | Diff |
|---|---|---|
| 242 | Valid Anagram | Easy |
| 347 | Top K Frequent Elements | Medium |
| 76 | Minimum Window Substring | Hard |
collections.deque
from collections import deque dq = deque([1, 2, 3]) dq.appendleft(0) # O(1) β unlike list.insert(0, x) dq.popleft() # O(1) β unlike list.pop(0) # BFS template queue = deque([start]) while queue: node = queue.popleft() for neighbor in graph[node]: queue.append(neighbor)
| # | Problem | Diff |
|---|---|---|
| 102 | Binary Tree Level Order Traversal | Medium |
| 127 | Word Ladder | Hard |
| 239 | Sliding Window Maximum | Hard |
heapq
import heapq h = [] heapq.heappush(h, 3) heapq.heappop(h) # returns smallest # Max heap β negate values heapq.heappush(h, -5) -heapq.heappop(h) # returns 5 heapq.heapify(nums) # in-place, O(n) heapq.nlargest(3, nums) # [5, 4, 3]
| # | Problem | Diff |
|---|---|---|
| 215 | Kth Largest Element in an Array | Medium |
| 23 | Merge K Sorted Lists | Hard |
| 295 | Find Median from Data Stream | Hard |
| 621 | Task Scheduler | Medium |
List Comprehensions Β· enumerate Β· zip
# List comprehension squares = [x**2 for x in range(10) if x % 2 == 0] # Grid init β DO NOT do [[0]*n]*m (aliasing bug!) grid = [[0] * n for _ in range(m)] # enumerate β index + value together for i, val in enumerate(nums): print(i, val) # zip β pair two lists, or transpose a matrix for a, b in zip(list1, list2): ... transposed = list(zip(*matrix)) # [(1,4),(2,5),(3,6)]
| # | Problem | Diff |
|---|---|---|
| 48 | Rotate Image | Medium |
| 54 | Spiral Matrix | Medium |
| 200 | Number of Islands | Medium |
sorted() with key=
# Sort by second element sorted(pairs, key=lambda x: x[1]) # Multi-key sort sorted(words, key=lambda w: (len(w), w)) # Descending sorted(nums, reverse=True) # In-place intervals.sort(key=lambda x: x[0]) # sort by start
| # | Problem | Diff |
|---|---|---|
| 56 | Merge Intervals | Medium |
| 435 | Non-overlapping Intervals | Medium |
| 253 | Meeting Rooms II | Medium |
dict.get(key, default)
d = {"a": 1}
d.get("b", 0) # 0, no KeyError
# One-liner frequency count
freq = {}
freq[c] = freq.get(c, 0) + 1
| # | Problem | Diff |
|---|---|---|
| 1 | Two Sum | Easy |
| 560 | Subarray Sum Equals K | Medium |
| 146 | LRU Cache | Medium |
String Methods
s = " hello world " s.strip() # "hello world" s.split() # ["hello", "world"] " ".join(["a","b","c"]) # "a b c" s[::-1] # reverse "abc".isalpha() # True "123".isdigit() # True "abc123".isalnum() # True
| # | Problem | Diff |
|---|---|---|
| 125 | Valid Palindrome | Easy |
| 151 | Reverse Words in a String | Medium |
| 5 | Longest Palindromic Substring | Medium |
set Operations
a = {1, 2, 3}
b = {2, 3, 4}
a | b # union {1,2,3,4}
a & b # intersect {2,3}
a - b # difference {1}
# O(1) lookup β prefer over list for membership checks
seen = set()
if x not in seen:
seen.add(x)
| # | Problem | Diff |
|---|---|---|
| 217 | Contains Duplicate | Easy |
| 128 | Longest Consecutive Sequence | Medium |
| 141 | Linked List Cycle | Easy |
bisect
import bisect nums = [1, 3, 4, 7, 9] bisect.bisect_left(nums, 4) # 2 β first index >= 4 bisect.bisect_right(nums, 4) # 3 β first index > 4 bisect.insort(nums, 5) # insert, keeping sorted β O(n) # Check membership in O(log n) idx = bisect.bisect_left(nums, x) found = idx < len(nums) and nums[idx] == x
| # | Problem | Diff |
|---|---|---|
| 981 | Time Based Key-Value Store | Medium |
| 300 | Longest Increasing Subsequence | Medium |
| 315 | Count of Smaller Numbers After Self | Hard |
| "top K" / "kth largest" | heapq |
| BFS / shortest path | deque |
| frequency / anagram | Counter |
| graph adjacency / grouping | defaultdict(list) |
| sorted input + O(log n) search | bisect |
| membership / dedup | set |
| sort by custom rule | sorted(key=...) |